//*************************************************************************************
/** \file task_wind.cc
 *    This file contains a task which reads a wind sensor. The sensor uses a 
 *    timer/counter in the AVR to add up pulses from the anemometer; to find wind 
 *    speed, the number of pulses counted by the counter is read and compared to the 
 *    number of pulses counted some time ago, giving an average rate of pulses per 
 *    time period. If being used on a particular unit, a wind sensor based on a US
 *    Digital analog "encoder" (a magnetically based potentiometer replacement) is used
 *    to find wind direction; the direction is processed as a pair of trigonometric 
 *    functions, the sine and the cosine of the wind direction angle. Both wind speed 
 *    and wind direction are averaged over time; the average is read (the averaging 
 *    sums automatically being set to zero) periodically by another task. 
 *
 *  Revisions:
 *    \li 12-26-2007 JRR Original file, with wind speed counting working only
 *    \li 12-27-2007 JRR Got the wind direction stuff working
 *    \li 03-20-2009 JRR Removed code for AAG sensors, which aren't reliable enough
 */
//*************************************************************************************

#include <stdlib.h>
#include <avr/io.h>
#include <avr/pgmspace.h>					// For reading table from program memory
#include "avr_1wire.h"
#include "base_text_serial.h"				// For writing things to serial devices
#include "global_debug.h"					// For printing debugging messages
#include "stl_timer.h"
#include "task_wind.h"
#include "ma3vane.h"						// The array of trig functions for vane


//-------------------------------------------------------------------------------------
/** This constructor creates a new wind measuring task object. The user must have
 *  previously created an objects of class avr_ds2423 and avr_ds2450 which run the 
 *  A/D and counter chips that do the measurement.
 *  @param a_timer A reference to the task timer
 *  @param interval_time A time stamp containing the time interval between runs
 *  @param p_a2d A pointer to a driver for the AVR's A/D converter, if used
 */

task_wind::task_wind (task_timer& a_timer, time_stamp interval_time, adc_10bit* p_adc)
    : stl_task (a_timer, interval_time)
{
	my_adc = p_adc;							// Save pointer to A/D converter

	TCCR1A = 0x00;
	TCCR1B = (1 << CS12) | (1 << CS11);		// Counter, falling edge
	TCNT1 = 0;								// Zero the counter on startup
	previous_count = 0;						// Zero the previous speed count
    TCCR1B |= (1 << ICNC1);					// Turn on input noise canceler
	DDRB &= ~0x02;							// Set pin as input
	PORTB |= 0x02;							// Turn on the pullup resistor

	// Reset all the averaging variables
	reset_sums ();

// 	GLOB_DEBUG ("Wind task constructor" << endl);
}


//-------------------------------------------------------------------------------------
/** This method resets the variables which are used to compute statistical information
 *  about wind measurements: maxima, minima, and averages. 
 */

void task_wind::reset_sums (void)
{
	// Start out the most recent readings at zero
	most_recent_speed = 0;
	most_recent_cosine = 0;
	most_recent_sine = 0;

	// Zero the averaging registers
	sum_of_speeds = 0L;
	sum_of_cosines = 0;
	sum_of_cosines = 0;
	sum_of_sines = 0;
	num_readings = 0;

	// Initialize the maximum and minimum speeds
	high_speed = 0;
	low_speed = 999;
}


//-------------------------------------------------------------------------------------
/** This method looks up the cosine of a wind direction angle in a table. The angle is
 *  first converted from a 0-255 full circle to one of four quadrants, then the cosine
 *  is looked up.
 *  @param angle The angle whose sine is to be found, from 0 to 255 for a full circle
 *  @return The cosine of the angle
 */

signed char task_wind::cosine (unsigned char angle)
{
	bool negative = false;					// This will be true for a negative result

	// The bottom half of a circle is a mirror image of the top half
	if (angle > 127)
		angle = (unsigned char)(256 - (unsigned int)angle);

	// Now check if the angle is in the first or second quadrant
	if (angle > 63)
	{
		negative = true;
		angle = 127 - angle;
	}

	// Get the angle out of the trig table
	signed char result = pgm_read_byte (&(ma3cosines[angle]));

	// Return the result, either negative or positive depending on the quadrant
	if (negative)
		return (-result);
	else
		return (result);
}


//-------------------------------------------------------------------------------------
/** This method looks up the sine of a wind direction angle in a table. It actually 
 *  asks the cosine function to do most of the work; first 64 (which means pi/2 rad) 
 *  is subtracted from the angle, then the cosine is called. 
 *  @param angle The angle whose sine is to be found, from 0 to 255 for a full circle
 *  @return The sine of the angle
 */

signed char task_wind::sine (unsigned char angle)
{
	// Note that if the subtraction underflows, we just interpret the result as 
	// positive anyway, so the angles work out correctly
	angle -= 64;
	return (cosine (angle));
}


//-------------------------------------------------------------------------------------
/** This method checks the battery voltage using the voltage divider attached to the 
 *  A/D converter. It is meant to be called by the radio task. 
 */

unsigned int task_wind::check_battery (void)
{
	#ifdef BATTERY_CHANNEL
		return (my_adc->read_n_times (BATTERY_CHANNEL, 10));
	#else
		return (0);
	#endif
}


//-------------------------------------------------------------------------------------
/** This is the function which runs when it is called by the task scheduler. It gets a
 *  count from the counter and computes a speed "reading" in pulses per time period; 
 *  it gets an A/D reading from the wind vane sensor (if used) and uses it to estimate
 *  trig functions of the direction (using a table lookup). Sums are added up so that
 *  average values can be computed later. 
 *  @param state The state of the task when this run method begins running
 *  @return The state to which the task will transition, or STL_NO_TRANSITION if no
 *      transition is called for at this time
 */

char task_wind::run (char state)
{
// 	GLOB_DEBUG ("Wind: ");

	unsigned int current_count = TCNT1;
	most_recent_speed = (int)(current_count - previous_count);
	previous_count = current_count;
	if (most_recent_speed > high_speed)
		high_speed = most_recent_speed;
	if (most_recent_speed < low_speed)
		low_speed = most_recent_speed;

	// If testing the C3, print out the reading every time we get one
	#ifdef TEST_C3
		GLOB_DEBUG (most_recent_speed << endl);
	#else
		GLOB_DEBUG ("[T1:" << hex << current_count << dec << "]");
	#endif

	// If we're using an MA3 based wind vane, read it now
	#ifdef USE_VANE_MA3
		uint8_t wind_angle = (uint8_t)(my_adc->read_n_times (4, 10) / 4);
		most_recent_cosine = cosine (wind_angle);
		most_recent_sine = sine (wind_angle);
// 		GLOB_DEBUG ("A/D " << wind_angle << " cos " << most_recent_cosine
// 			<< " sin " << most_recent_sine);
	#endif

	// Save the readings to the averaging registers for averaging out later
	if (num_readings < 255)
	{
		num_readings++;
		sum_of_speeds += most_recent_speed;
		sum_of_cosines += most_recent_cosine;
		sum_of_sines += most_recent_sine;
	}

// 	GLOB_DEBUG (endl);

	return (STL_NO_TRANSITION);
}


//-------------------------------------------------------------------------------------
/** This method computes and returns the average reading from the number of readings 
 *  and the sum of those readings' values. These sums have been saved up since the
 *  last time this method was called. If there hasn't been any data since the method
 *  reset_sums() was last called, this method returns 0. 
 *  @return The average wind speed since the averaging sum was last zeroed
 */

unsigned int task_wind::get_average_speed (void)
    {
    return ((int)(sum_of_speeds / num_readings));
    }


//-------------------------------------------------------------------------------------
/** This method returns the most recently measured speed in counts (2 per revolution 
 *  of the anemometer cup unit). 
 *  @return The most recently read temperature
 */

unsigned int task_wind::get_latest_speed (void)
    {
    return (most_recent_speed);
    }


//-------------------------------------------------------------------------------------
/** This method returns the maximum speed measured since the averaging sums were last 
 *  set to zero.
 *  @return The maximum (gust) speed reading
 */

unsigned int task_wind::get_maximum_speed (void)
    {
    return (high_speed);
    }


//-------------------------------------------------------------------------------------
/** This method returns the minimum speed measured since the averaging sums were last 
 *  set to zero.
 *  @return The maximum (lull) speed reading
 */

unsigned int task_wind::get_minimum_speed (void)
    {
    return (low_speed);
    }


//-------------------------------------------------------------------------------------
/** This method computes the average direction reading from the wind vane. One can't
 *  just average an angle (bad things happen where it crosses 360 degrees), so instead
 *  we average the sine and cosine numbers, which are being stored anyway. If the 
 *  components returned are both zero, there's no valid direction; this probably 
 *  indicates that no data has been taken since the last time this function was called,
 *  because when this function is called, the averaging sums are set to zero. 
 *  @param cosine The trig component parallel to the arbitrary zero degree line
 *  @param sine The trig component perpendicular to the arbitrary zero degree line
 */

void task_wind::get_average_direction (char* cosine, char* sine)
    {
    *cosine = (char)(sum_of_cosines / num_readings);
    *sine = (char)(sum_of_sines / num_readings);
    }


//-------------------------------------------------------------------------------------
/** This method returns the most recently measured wind direction from the wind vane. 
 *  The direction is returned as a sine and a cosine number; the ratio of these two
 *  can be used to find what the actual direction is. The sine/cosine data is used in
 *  order to permit readings to be averaged over time. If the readings returned are 
 *  both zero, there's no valid direction; this indicates that something is amiss. 
 *  @param cosine The trig component parallel to the arbitrary zero degree line
 *  @param sine The trig component perpendicular to the arbitrary zero degree line
 */

void task_wind::get_latest_direction (char* cosine, char* sine)
    {
    *cosine = most_recent_cosine;
    *sine = most_recent_sine;
    }
